﻿#region Librerias

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using ESF.General.Network.ConfigSettings;
using System.Configuration;
using System.Net.NetworkInformation;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Web.Services.Protocols;

#endregion


namespace ESF.General.Network
{
	public static class ConnectionTesterHelper
	{
		public const int WEBPORT = 80;


		public static bool TestConnection (string pOriginalDestination, IPEndPoint pEndPoint, ref StringBuilder pResult)
		{
			Socket socketClient = null;


			pResult.AppendLine (new String ('-', 40));
			pResult.AppendFormat ("Verificando la conectividad a:");
			pResult.AppendLine ();
			pResult.AppendFormat ("{0} / {1}:{2}", pEndPoint, pOriginalDestination, pEndPoint.Port);
			pResult.AppendLine ();
			pResult.AppendLine (DateTime.Now.ToString ("yyyy-MM-dd HH:mm:ss"));
			pResult.AppendLine ();
			
			
			try
			{
				socketClient = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
				socketClient.NoDelay = true;
				
				pResult.AppendFormat ("Conectando... ");
				socketClient.Connect (pEndPoint);	
				pResult.AppendFormat (" [ OK ]");
				pResult.AppendLine ();

				pResult.AppendFormat ("Desconectando...");
				socketClient.Disconnect (false);
				pResult.AppendFormat (" [ OK ]");
				pResult.AppendLine ();

				return true;
			}
			catch (Exception pEx)
			{
				AppendError ("Su equipo no se puede conectar de forma directa, indique a su administrador de red que permita esta conexión", pEx.Message, ref pResult);
			}
			finally
			{
				if (socketClient != null && socketClient.Connected)
				{
					socketClient.Close ();
				}
			}

			return false;
		}


		private static void AppendError (string pInfo, string pMessage, ref StringBuilder pResult)
		{
			IPInterfaceProperties ipProp;


			pResult.AppendLine (new String ('-', 40));
			pResult.AppendLine ();
			pResult.AppendLine ("QUE DEBO HACER?");
			pResult.AppendLine (pInfo);
			pResult.AppendLine ();
			pResult.AppendLine (new String ('-', 40));
			pResult.AppendLine ();
			pResult.AppendLine ("DETALLE DEL ERROR:");
			pResult.AppendLine (pMessage);
			pResult.AppendLine ();
			pResult.AppendLine (new String ('-', 40));
			pResult.AppendLine ("Indique al administrador de su red que directamente desde su maquina no es posible realizar esta conexión.");
			pResult.AppendLine ("Sus tarjetas de red (activas) son: ");

			foreach (NetworkInterface ni in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces ())
			{
				if (ni.OperationalStatus == OperationalStatus.Up)
				{
					pResult.AppendLine (new String ('-', 30));
					pResult.AppendFormat ("{0} ({1})", ni.Name, ni.OperationalStatus);
					pResult.AppendLine ();

					ipProp = ni.GetIPProperties ();
				
					pResult.AppendLine (new String ('-', 20));
					pResult.AppendLine ("Direcciones de IP: ");
					pResult.AppendLine (new String ('-', 20));

					foreach (IPAddressInformation ip in ipProp.UnicastAddresses)
					{
						pResult.AppendFormat ("{0}, ", ip.Address);
					}

					pResult.Length -= 2;

					pResult.AppendLine ();
					pResult.AppendLine (new String ('-', 20));
					pResult.AppendLine ("Direcciones de DNS: ");
					pResult.AppendLine (new String ('-', 20));

					foreach (IPAddress dns in ipProp.DnsAddresses)
					{
						pResult.AppendFormat ("{0}, ", dns);
					}

					pResult.Length -= 2;

					pResult.AppendLine ();
					pResult.AppendLine (new String ('-', 30));
				}
			}
		}


		public static bool TestWSConnection (string pWSURI, WebRequestHelper.RequestMethod pMethod, NameValueCollection pParams, ref StringBuilder pResult)
		{
			long ellapsedMs;

			string contents, statusDescription;

			HttpStatusCode statusCode;

			bool isProxyByPassed;

			WebRequest webRequest;

			Uri webProxyUri;

			
			pResult.AppendLine (new String ('-', 40));
			pResult.AppendFormat ("Probando el acceso al Servicio Web:");
			pResult.AppendLine ();
			pResult.AppendFormat ("Dirección: {0}", pWSURI);
			pResult.AppendLine ();
			pResult.AppendLine (DateTime.Now.ToString ("yyyy-MM-dd HH:mm:ss"));
			pResult.AppendLine ();
			pResult.AppendFormat ("Invocando el servicio ({0} parametros)...", ((pParams == null || pParams.Count == 0) ? "sin" : "con"));
			pResult.AppendLine ();

			webRequest = WebRequestHelper.CreateRequest (pWSURI, pMethod, pParams);

			webProxyUri = WebRequestHelper.GetWebProxy (webRequest, out isProxyByPassed);

			AppendProxy (webProxyUri, isProxyByPassed, ref pResult);

			try
			{
				contents = WebRequestHelper.GetResponse (webRequest, pParams, out ellapsedMs, out statusCode, out statusDescription);
				
				pResult.AppendFormat ("Obtenidos {0}, en {1} [{2}/s promedio] con un Timeout de {3} metodo {4}", 
					TextHelper.ToStringBytes (contents.Length),
					TextHelper.ToStringMS (ellapsedMs),
					TextHelper.ToStringBytes ((long) Math.Ceiling (contents.Length / (ellapsedMs / 1000.0))),
					TextHelper.ToStringMS (webRequest.Timeout), 
					webRequest.Method);

				pResult.AppendLine ();
				pResult.AppendFormat ("Detalle: Código {0}, Descripción {1}", statusCode, statusDescription);
				pResult.AppendLine ();
			
				return true;
			}
			catch (Exception pEx)
			{
				AppendError (
					string.Format ("Indique a su administrador de red que ajuste {0}", (webProxyUri == null) ? "la conexion directa al servidor web" : "el proxy para permitir la conexión al servidor web"),
					pEx.Message, ref pResult);

				return false;
			}
		}


		public static void AppendProxy (Uri pWebProxyUri, bool pIsProxyByPassed, ref StringBuilder pResult)
		{
			if (pWebProxyUri != null)
			{
				pResult.AppendFormat ("El proxy es: {0}", pWebProxyUri);

				if (pIsProxyByPassed)
				{
					pResult.AppendFormat (" (no es utilizado para esta dirección)");
				}
			}
			else
			{
				pResult.AppendFormat ("La conexión es directa");
			}			

			pResult.AppendLine ();
		}


		public static bool TestWebConnection (string pOriginalDestination, string pWebURL, ref StringBuilder pResult)
		{
			WebRequest webClient;

			bool isProxyByPassed;

			string contents;

			Uri webProxyUri;

			long ellapsedMs;

			HttpStatusCode statusCode;

			string statusDescription;

			
			pResult.AppendLine (new String ('-', 40));
			pResult.AppendFormat ("Probando el acceso Web a:");
			pResult.AppendLine ();
			pResult.AppendFormat ("Dirección: {0} / {1}", pWebURL, pOriginalDestination);
			pResult.AppendLine ();
			pResult.AppendLine (DateTime.Now.ToString ("yyyy-MM-dd HH:mm:ss"));
			pResult.AppendLine ();

			webClient = WebRequestHelper.CreateRequest (pWebURL.ToString ());
			
			pResult.AppendFormat ("Identificando el proxy del sistema... ");
			
			webProxyUri = WebRequestHelper.GetWebProxy (webClient, out isProxyByPassed);

			AppendProxy (webProxyUri, isProxyByPassed, ref pResult);

			
			try
			{
				contents = WebRequestHelper.GetResponse (webClient, null, out ellapsedMs, out statusCode, out statusDescription);

				pResult.AppendFormat ("Descargados {0} de la página", TextHelper.ToStringBytes (contents.Length));
				
				pResult.AppendFormat (", en {0} [{1}/s promedio] con un timeout de {2}, metodo {3}", 
					TextHelper.ToStringMS (ellapsedMs),
					TextHelper.ToStringBytes ((long) (Math.Ceiling (contents.Length / (ellapsedMs / 1000.0)))),
					TextHelper.ToStringMS (webClient.Timeout), webClient.Method);

				pResult.AppendLine ();
				pResult.AppendFormat ("Detalle: Código {0}, Descripción {1}", statusCode, statusDescription);
				pResult.AppendLine ();

				return true;
			}
			catch (Exception pEx)
			{
				AppendError (
					string.Format ("Indique a su administrador de red que ajuste {0}", (webProxyUri == null) ? "la conexion directa al servidor web" : "el proxy para permitir la conexión al servidor web"),
					pEx.Message, ref pResult);

				return false;
			}
		}


		public static bool TestAppConfigurationSections (out string pResult)
		{
			ConnectionTestingSection cnnTestingSection;

			IPEndPoint endPoint;

			StringBuilder result;

			bool allOK = true;

			string originalDestination;


			result = new StringBuilder ();

			result.AppendFormat ("Inicia la revisión...");
			result.AppendLine ();
			result.AppendLine (DateTime.Now.ToString ("yyyy-MM-dd HH:mm:ss"));
			result.AppendLine ();

			try
			{
				cnnTestingSection = ConfigurationManager.GetSection ("ConnectionTesting") as ConnectionTestingSection;


				if (cnnTestingSection == null || cnnTestingSection.ConnectionTests.Count == 0)
				{
					result.AppendLine ("Error: No han sido definidos puntos de conexión para probar en el archivo de configuración");
					pResult = result.ToString ();

					return false;
				}

				foreach (ConnectionTestElement item in cnnTestingSection.ConnectionTests)
				{
					result.AppendFormat ("Probando la conectividad para {0}", item.name);
					result.AppendLine ();

					if (!string.IsNullOrEmpty (item.WSAddress))
					{
						allOK = ConnectionTesterHelper.TestWSConnection (
								item.WSAddress, 
								(WebRequestHelper.RequestMethod) Enum.Parse (typeof (WebRequestHelper.RequestMethod), item.httpMethod), 
								item.GetParameters (), 
								ref result)
							&& allOK;
					}
					else 
					{
						endPoint = item.GetIPEndPoint (out originalDestination);
						
						if (item.port == ConnectionTesterHelper.WEBPORT || item.isWeb)
						{
							allOK = ConnectionTesterHelper.TestWebConnection (originalDestination, 
								string.Format ("http://{0}:{1}", endPoint.Address, endPoint.Port), ref result) 
								&& allOK;
						}
						else
						{
							allOK = ConnectionTesterHelper.TestConnection (originalDestination, endPoint, ref result) 
								&& allOK;
						}
					}

					result.AppendLine ();
					result.AppendLine ();
				}

				pResult = result.ToString ();

				return allOK;
			}
			catch (Exception pEx)
			{
				AppendError ("Ajuste los parametros del archivo de configuración", pEx.Message, ref result);

				pResult = result.ToString ();
				
				return false;
			}
		}
	}
}
